03. Functional Mixins

Remember Constructor Functions?

We previously used a constructor function to create a new object:

function City(name, population) {
  this.name = name;
  this.population = population;

  this.identify = function () {
    console.log(`${this.name}'s population is ${this.population}.`);
  };
}

To instantiate, we invoke the function with the new operator:

const sanFrancisco = new City('San Francisco', 870000);

console.log(sanFrancisco);
// {
//   name: 'San Francisco',
//   population: 870000,
//   identify: function () {
//     console.log(`${this.name}'s population is ${this.population}.`);
//   };
// }

We can use the same constructor to create multiple objects:

const mountainView = new City('Mountain View', 78000);

console.log(mountainView);
// {
//   name: 'Mountain View',
//   population: 78000,
//   identify: function () {
//     console.log(`${this.name}'s population is ${this.population}.`);
//   };
// }

Again, note that we used the new keyword each time to create a new object. Let's now shift gears a bit to factory functions which produce object instances without the use of the new operator!

Factory Functions

A factory function is a function that returns an object, but isn't itself a class or constructor. As such, we invoke a factory function as a normal function without using the new operator. Using a factory function, we can easily create object instances without the complexity of classes and constructors!

Check out the following Basketball() factory function:

function Basketball(color) {
  return {
    color: color,
    numDots: 35000
  };
}

What's important to note here is that Basketball() returns an object directly. This is different from a constructor function which returns its object automatically.

Let's invoke Basketball() and check out its output:

const orangeBasketball = Basketball('orange');

console.log(orangeBasketball);
// { color: 'orange', numDots: 35000 }

A factory function has its name because, just like a chair factory can produce chair after chair after chair, a factory function can be used over and over to create any number of objects:

const myBB = Basketball('blue and green');
const yourBB = Basketball('purple');
const bouncy = Basketball('neon pink');

Great! Invoking the factory function allows us to compose a single object -- all without the use of the new operator. Before we take a look at a more complex example, let's summarize the differences between a factory function and a constructor function:

_Comparing and contrasting factory functions and constructor functions_

Comparing and contrasting factory functions and constructor functions

22 - SC -- Factory Functions With Closure

Functional Mixins

In the previous section, we used mixins to add features into a composite object. We also just leveraged factory functions to create objects without using the new operator or messing with prototypal inheritance. Let's combine what we've learned from mixins and factory functions and take things a step further with functional mixins!

A functional mixin is a composable factory function that receives a _mixin_as an argument, copies properties and methods from that mixin, and returns a new object. Check out the following example: CoffeeMaker():

function CoffeeMaker(object) {
  let needsRefill = false;

  return Object.assign({}, object, {
    pourAll: function () {
      needsRefill = true;
    },
    isEmpty: function () {
      return needsRefill;
    }
  });
}

Note that unlike a standard factory function, which takes in individual property values as arguments -- the functional mixin actually takes in an object itself! Whichever object is passed in to the function, is merged with other objects passed into Object.assign().

Let's pass the following percolator object into CoffeeMaker() and view the results:

const mixedCoffeeMaker = CoffeeMaker({ style: 'percolator' });

The returned mixedCoffeeMaker object now looks like:

{
  style: 'percolator',
  pourAll: function () {
    needsRefill = true;
  },
  isEmpty: function () {
    return needsRefill;
  }
}

Now, one of the great things about functional mixins is that they are composable; we can use them as individual pieces of code that add specific properties like an assembly line. Let's take a closer look!

25 - SC - Functional Mixins

Quiz: Factory Functions and Mixins

What is true about factory functions or mixins? Select all that apply:

SOLUTION:
  • Just like a constructor function, a factory function can be called multiple times to create an object

Summary

A factory function creates objects. It is invoked as normal function, not with the new operator. Functional mixins take things a bit further by accepting a mixin as an argument, copies properties and methods from the mixin, and returns a new object.